#pragma once

#include <fstream>

#include "GNSSMesRecordRead.hpp"
#include "GNSSMesHeaderRead.hpp"
#include "GNSSMesDecode.hpp"
#include "GNSSMesPackage.hpp"
#include "../../Utility/Log/Log.hpp"
#include "../../Utility/Data/Time/TimeFactory.hpp"

namespace ns_GNSSMesInput
{
    using namespace ns_Log;
    using namespace ns_Time;

#define ALL_GNSSTYPE {GNSSType::BDS, GNSSType::GPS}

    class GNSSMesInput
    {
    public:
        GNSSMesInput()
        {
        }

    public:
        static bool Save(const std::string &fileName, const std::string &content)
        {
            std::ofstream outStream(PathUtil::Src(fileName), std::ios::app);
            if (!outStream.is_open())
                return false;

            outStream.write(content.c_str(), content.size());
            return true;
        }

        static bool Start(const std::string &inJson)
        {
            Json::Reader reader;
            Json::Value inValue;

            if (!reader.parse(inJson, inValue))
            {
                return false;
            }

            std::string fileName = inValue["FileName"].asString();

            if (fileName.empty())
                return false;

            if (!FileUtil::IsFileExist(PathUtil::Src(fileName)))
            {
                Log(Debug) << "需要读取的文件不存在！" << '\n';
                return false;
            }

            if (FileUtil::IsFileExist(PathUtil::Dir(fileName)))
            {
                Log(Debug) << "文件已被读取" << '\n';
                return true;
            }

            GNSSMesHeaderReader headerReader;
            GNSSMesRecordReader recordReader;
            GNSSMesHeaderDecoder headerDecoder;
            GNSSMesRecordDecoder recordDecoder;
            GNSSMesPackager packeager;

            std::unordered_map<EndLineMarkType, std::vector<std::vector<std::string>>> headerReadResult;
            headerReader.Read(fileName, ALL_GNSSTYPE, &headerReadResult);

            std::vector<GNSSMesHeader::Ptr> headerDecodeResult((int)GNSSType::DefaultGNSSType, nullptr);
            for (const auto &e : headerReadResult)
            {
                EndLineMarkType lineType = e.first;
                for (const auto &v : e.second)
                {
                    GNSSMesHeader::Ptr headerPtr;
                    switch (lineType)
                    {
                    case EndLineMarkType::HeaderPre:
                        headerDecoder.HeaderPreDecode(v, &headerPtr);
                        break;
                    case EndLineMarkType::IonoCorr:
                        headerDecoder.IonoCorrDecode(v, &headerPtr);
                        break;
                    case EndLineMarkType::TimeCorr:
                        headerDecoder.TimeCorrDecode(v, &headerPtr);
                        break;
                    case EndLineMarkType::LeapSecond:
                        headerDecoder.LeapSecondDecode(v, &headerPtr);
                        break;
                    default:
                        break;
                    }

                    GNSSType type = headerPtr->gnssType;
                    if (type == GNSSType::DefaultGNSSType)
                        continue;

                    if (headerDecodeResult[type] == nullptr)
                    {
                        headerDecodeResult[type] = std::make_shared<GNSSMesHeader>();
                        headerDecodeResult[type]->gnssType = type;
                    }

                    Merge(headerDecodeResult[type], headerPtr);
                }
            }

            for(auto e:headerDecodeResult)
            {
                if(e!=nullptr)
                {
                    e->version = headerDecodeResult[0]->version;
                    e->leapSecond = headerDecodeResult[0]->leapSecond;
                }
            }

            packeager.PackgeHeader(fileName, headerDecodeResult);

            std::vector<std::vector<std::string>> readResult;
            recordReader.Read(fileName, ALL_GNSSTYPE, &readResult);

            std::vector<GNSSMesRecord::Ptr> recordDecodeResult;
            for (const auto &e : readResult)
            {
                GNSSMesRecord::Ptr recordPtr;
                recordDecoder.Decode(e, &recordPtr);
                recordDecodeResult.push_back(recordPtr);
            }

            packeager.PackgeRecord(fileName, recordDecodeResult);

            return true;
        }

        static bool GetRecords(const std::string &inJson, std::string *outJson)
        {
            Json::Reader reader;
            Json::Value inValue;

            if (!reader.parse(inJson, inValue))
                return false;

            std::string fileName = inValue["FileName"].asString();
            std::string GNSSTypeString = inValue["GNSSType"].asString();
            GNSSType gnssType = GNSSTypeUtil::GetGNSSTypeByName(GNSSTypeString);
            int serial = inValue["Serial"].asInt();

            std::string path = PathUtil::Record(fileName, gnssType, serial);
            FileUtil::ReadFromFile(path, outJson, true);

            return true;
        }

        static bool GetNearestRecord(const std::string &inJson, std::string *outJson)
        {
            Json::Reader reader;
            Json::Value inValue;

            if (!reader.parse(inJson, inValue))
                return false;

            std::string fileName = inValue["FileName"].asString();
            std::string GNSSTypeString = inValue["GNSSType"].asString();
            GNSSType gnssType = GNSSTypeUtil::GetGNSSTypeByName(GNSSTypeString);
            if(gnssType==GNSSType::DefaultGNSSType)
            {
                Log(Debug)<<"未知的GNSS类型"<<'\n';
                return false;
            }
            int serial = inValue["Serial"].asInt();
            std::string curTimeJson = JsonUtil::ValueToString(inValue["CurTime"]);
            auto curTime = std::make_shared<CommonTime>();
            curTime->Decode(curTimeJson);


            std::string recordsJson;
            std::string path = PathUtil::Record(fileName, gnssType, serial);
            FileUtil::ReadFromFile(path, &recordsJson);

            if(recordsJson.empty())
            {
                return false;
            }


            auto records = JsonUtil::StringToValue(recordsJson);
            int size = records["Size"].asInt();
            for(int i =0;i<size;i++)
            {
                auto recordJson = JsonUtil::ValueToString(records["Content"][i]);
                GNSSMesRecord::Ptr record = std::make_shared<GNSSMesRecord>();
                record->Decode(recordJson);

                auto recordTime = TimeFactory::MakeTimePtr(TimeFactory::ValueType::CommonTimeSys,record->year,record->month,record->day,record->hour,record->minute,record->second);
                if(abs(recordTime->SecondFromRefTime()-curTime->SecondFromRefTime())<=3600)
                {
                    record->Encode(outJson);
                    return true;
                }
            }

            return false;
        }

        static bool GetHeader(const std::string &inJson, std::string *outJson)
        {
            Json::Reader reader;
            Json::Value inValue;

            if (!reader.parse(inJson, inValue))
                return false;

            std::string fileName = inValue["FileName"].asString();
            std::string GNSSTypeString = inValue["GNSSType"].asString();
            GNSSType gnssType = GNSSTypeUtil::GetGNSSTypeByName(GNSSTypeString);

            std::string path = PathUtil::Header(fileName, gnssType);
            FileUtil::ReadFromFile(path, outJson, true);

            return true;
        }

        static bool RemoveTempFiles(const std::string &inJson)
        {
            Json::Reader reader;
            Json::Value inValue;

            if (!reader.parse(inJson, inValue))
            {
                return false;
            }

            std::string fileName = inValue["FileName"].asString();

            if (!FileUtil::RemoveFilesInDirectory(PathUtil::Dir(fileName)))
                return false;

            rmdir(PathUtil::Dir(fileName).c_str());

            return true;
        }

    private:
        static void Merge(GNSSMesHeader::Ptr &input, const GNSSMesHeader::Ptr &plus)
        {
            if (input->gnssType != plus->gnssType)
                return;

            if (input->version.empty())
                input->version = plus->version;

            if (input->leapSecond == 0)
                input->leapSecond = plus->leapSecond;

            if (input->ionAlpha[0] == 0&&input->ionAlpha[1] == 0&&input->ionAlpha[2] == 0&&input->ionAlpha[3] == 0)
                memcpy(&(input->ionAlpha), &(plus->ionAlpha), sizeof(double) * 4);

            if (input->ionBeta[0] == 0&&input->ionBeta[1] == 0&&input->ionBeta[2] == 0&&input->ionBeta[3] == 0)
                memcpy(&(input->ionBeta), &(plus->ionBeta), sizeof(double) * 4);

            if (input->timeSysCorr.A0 == 0&&input->timeSysCorr.A1==0&&input->timeSysCorr.T==0&&input->timeSysCorr.W==0)
                memcpy(&(input->timeSysCorr), &(plus->timeSysCorr), sizeof(double) * 4);
        }
    };
}